Hola que tal. En esta serie de posts veremos el uso de las expresiones lambda en Java, las cuales nos acercan un poco a nosotros (los programadores de java) al paradigma funcional, ayudando a reducir la cantidad de código necesario y es mucho mas fácil de entender. Las expresiones lambda pueden representar funciones anónimas, es decir, funciones que no necesitan una clase (aunque tras bambalinas no es verdad ya que Java es un lenguaje en donde no existe algo como un elemento de tipo función anónima, mas bien lo que representan es una interfaz funcional, mas adelante veremos que es eso)
Antes de entrarle de lleno a las expresiones lamba, considero que es necesario entender el concepto de “Interfaz Funcional”. Una interfaz funcional en Java es aquella interfaz que tiene solo un método abstracto, se puede tener muchos métodos pero solo uno debe ser abstracto. A continuación un ejemplo de interfaz funcional:
public interface MyInterface {
void metodo1();
// public abstract void metodo1();
}
En el código anterior podemos ver que hay otro metodo1 pero esta comentado y tiene public abstract. Si descomentamos ese método y comentamos el otro método, todo seria exactamente igual. ¿Por qué? Pues porque los métodos en una interfaz que no tienen un cuerpo (que no tiene algo entre llaves { } después de su definición) son por defecto public y abstract, por lo que poner esas 2 palabras es redundante.
Nos podemos apoyar de la anotación @FunctionalInterface para asegurarnos en tiempo de compilación si una interfaz es en realidad funcional o no, ósea que si anotamos una interfaz con @FunctionalInterface y resulta que no es una interfaz funcional, no se podrá compilar nuestro programa.
@FunctionalInterface
public interface MyInterface {
void metodo1();
}
// Esta interfaz no compilará porque tiene mas de un método abstracto.
@FunctionalInterface
public interface MyInterfaceV2 {
void metodo1();
void metodo2();
}
Ojo aquí y que quede muy claro: El hecho de que una interfaz tenga la anotación @FunctionalInterface, no la hace funcional. Recalco esto porque he visto que algunos se quedan con la idea de que si no se tiene esa anotación no es una interfaz funcional. @FunctionalInterface es únicamente como un check que revisa en tiempo de compilación si nuestra interfaz es funcional, una interfaz puede ser funcional sin tener esa anotación. Mucho ojo con eso.
Si una interfaz, además de tener un método abstracto, tiene mas métodos que no sean abstractos, dicha interfaz sigue siendo una interfaz funcional. El chiste es tener un método abstracto. A continuación se muestra un ejemplo de una interfaz funcional con muchos métodos pero solo uno es abstracto.
// Esta interfaz compilará perfectamente
@FunctionalInterface
public interface MyInterface {
void metodo1();
default void metodo2(){}
private void metodo3(){}
static void metodo4(){}
}
Otro dato interesante sobre las interfaces funcionales: Si la interfaz tiene un método abstracto que sea definido igual a un método de la clase Object entonces se ignora. Por ejemplo, la siguiente interfaz es una interfaz funcional porque, aunque tiene 2 métodos abstractos, uno de esos métodos tiene la misma definición que el método toString de la clase Object, por lo cual se ignora y solo se toma en cuenta el otro método abstracto llamado metodo1:
// Esta interfaz compilará perfectamente
@FunctionalInterface
public interface MyInterface {
void metodo1(); // Solo este método es considerado
String toString();
}
¿Y para que sirve todo esto de las interfaces funcionales? Pues resulta que las expresiones lambdas en Java se utilizan en donde se requiere el uso de una interfaz funcional.
Ahora si es momento de entrar de lleno a las expresiones lambda. Una expresion lambda tiene esta estructura:
( Parametros ) -> { Cuerpo de la Expresión }
La primera parte corresponde a los parámetros, después la flechita -> y finalmente el cuerpo de la expresión lambda. Cabe destacar que si la expresión lambda recibe solo un parámetro los paréntesis se pueden omitir, y si el cuerpo de la expresión lambda se compone de una sola sentencia las llaves se pueden omitir también. También si la expresión lambda es una expresión que regresa un valor y es de una sola linea, se puede omitir la palabra reservada return.
Para nuestro ejemplo haremos uso de una interfaz llamada Operación y crearemos las 4 operaciones básicas (suma, resta, multiplicación y división) usando dicha interfaz.
@FunctionalInterface
public interface Operacion {
int calcular(int a, int b);
}
A continuación se muestra la implementación:
public class Ejemplo {
public static void main(String[] args) {
Operacion suma = (a, b) -> a + b;
Operacion resta = (a, b) -> a - b;
Operacion multiplicacion = (a, b) -> a * b;
Operacion division = (a, b) -> a / b;
System.out.println(suma.calcular(10,10));
System.out.println(resta.calcular(10,10));
System.out.println(multiplicacion.calcular(10,10));
System.out.println(division.calcular(10,10));
}
}
El resultado de ejecutar este codigo seria lo siguiente:
20
0
100
1
Como se puede ver, no es necesario crear clases que implementen la interfaz Operacion, ni tampoco crear clases anónimas. Con las lambdas podemos definir el comportamiento de Operacion de manera mas corta y simple.